//
//  HTTPManager.swift
//  EasyDana
//
//  Created by mac on 2024/10/8.
//

import Foundation
import Alamofire
import UIKit
import AdSupport

struct JSONResponse<T: Codable>: Codable {
    
    enum CodingKeys: String, CodingKey {
        case code = "openly"
        case state = "lettres"
        case data = "belles"
    }
    let code: Int
    let state: String
    let data: T?
    
    init(from decoder: any Decoder) throws {
        let container: KeyedDecodingContainer<JSONResponse<T>.CodingKeys> = try decoder.container(keyedBy: JSONResponse<T>.CodingKeys.self)
        self.code = try container.decode(Int.self, forKey: JSONResponse<T>.CodingKeys.code)
        self.state = try container.decode(String.self, forKey: JSONResponse<T>.CodingKeys.state)
        do {
            self.data = try container.decodeIfPresent(T.self, forKey: JSONResponse<T>.CodingKeys.data)
        } catch {
            self.data = nil
        }
    }
}


class HTTPManager {
    
    
    struct ServerModel: Codable {
        let tu: String
    }
    
    static let `default` = HTTPManager()
    
    var config: InitializeDataModel?
    
    var server = "https://esd.khazana-tradelinks.com/stopped"
    
    var bakedServers: [ServerModel] = []
    
    var currentServerIndex = -1
    
    var sessionID: String {
        return UserDefaults.standard.string(forKey: "session_id") ?? ""
    }
    
    private let uploadQueue = DispatchQueue.global(qos: .default)
    
    func switchServer() -> Bool {
        if (self.bakedServers.count > 0) {
            if (currentServerIndex < self.bakedServers.count - 1) {
                currentServerIndex = currentServerIndex + 1
            } else {
                currentServerIndex = 0
            }
            self.server = self.bakedServers[currentServerIndex].tu
            return true
        } else {
            return false
        }
    }
    
    var defaultParam: String {
        let flush = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
        let blooded = DeviceManager.defalult.originPhoneModel
        let stockings = DeviceManager.defalult.idfv
        let holes = UIDevice.current.systemVersion
        let unhandsome = self.sessionID
        let dungarees = ASIdentifierManager.shared().advertisingIdentifier.uuidString
        let gallant = self.config?.gallant ?? 1//国家
        return "flush=\(flush)&blooded=\(blooded)&stockings=\(stockings)&holes=\(holes)&unhandsome=\(unhandsome)&dungarees=\(dungarees)&gallant=\(gallant)"
    }
    
    func enumerateServer() async throws  {
        
        if self.bakedServers.count > 0 {
            return
        }
        
        let url = "https://id3-dc.oss-ap-southeast-5.aliyuncs.com/easy-dana/easy.json"

        let request = AF.request(url, method: .get, parameters: nil, headers: nil)
        request
            .validate()
            .responseDecodable(of: [ServerModel].self) { response in
                switch (response.result) {
                case .success(let value):
                    self.bakedServers = value
                    break
                case .failure(let error):
                    print(error)
                    break
                }
            }
    }
    
    @discardableResult
    func get<T: Codable>(path: Api, param: [String: Any]?, dataType: T.Type) async throws -> JSONResponse<T> {
        let url = (self.server + path.rawValue + "?" + self.defaultParam).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? (self.server + path.rawValue + "?" + self.defaultParam)
        let header: HTTPHeaders = [:]
        let request = AF.request(url, method: .get, parameters: param, headers: header, requestModifier: {$0.timeoutInterval = 10})
//#if DEBUG
//request
//    .responseString { response in
//        switch (response.result) {
//        case .success(let value):
//            print(value)
//            break
//        case .failure(let error):
//            print(Api.server + path + "\(error)")
//            break
//        }
//    }
//#endif
        return try await withCheckedThrowingContinuation { continuation in
            request
                .validate()
                .responseDecodable(of: JSONResponse<T>.self) { response in
                    switch response.result {
                        case .success(let value):
                        if value.code == -2 {
                            GlobalNotifier.default.needLogin.onNext(true)
                        }
                            continuation.resume(returning: value)
                            break
                        case .failure(let error):
                            continuation.resume(throwing: error)
                            break
                    }
                    
                }
        }
    }
    
    @discardableResult
    func upload<T: Codable>(path: Api, u: URL, fileKey: String, param: [String: Any]?, dataType: T.Type) async throws -> JSONResponse<T> {
        let url = (self.server + path.rawValue + "?" + self.defaultParam).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? (self.server + path.rawValue + "?" + self.defaultParam)
        let request = AF.upload(multipartFormData: { multipart in
            multipart.append(u, withName: fileKey)
            if let p = param {
                for (key, value) in p {
                    let data = "\(value)".data(using: .utf8)
                    multipart.append(data!, withName: key)
                }
            }
        }, to: url)
//#if DEBUG
//request
//    .responseString { response in
//        switch (response.result) {
//        case .success(let value):
//            print(value)
//            break
//        case .failure(let error):
//            print(Api.server + path + "\(error)")
//            break
//        }
//    }
//#endif
        return try await withCheckedThrowingContinuation { continuation in
            request
                .validate()
                .responseDecodable(of: JSONResponse<T>.self) { response in
                    switch response.result {
                        case .success(let value):
                        if value.code == -2 {
                            GlobalNotifier.default.needLogin.onNext(true)
                        }
                            continuation.resume(returning: value)
                            break
                        case .failure(let error):
                            continuation.resume(throwing: error)
                            break
                    }
                    
                }
        }
    }
    
    @discardableResult
    func upload<T: Codable>(path: Api, data: Data?, fileKey: String?, param: [String: Any]?, dataType: T.Type) async throws -> JSONResponse<T> {
        let url = (self.server + path.rawValue + "?" + self.defaultParam).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? (self.server + path.rawValue + "?" + self.defaultParam)
        let request = AF.upload(multipartFormData: { multipart in
            if data != nil && fileKey != nil {
                multipart.append(data!, withName: fileKey!, fileName: fileKey!, mimeType: "jpeg")
            }
            if let p = param {
                for (key, value) in p {
                    let data = "\(value)".data(using: .utf8)
                    multipart.append(data!, withName: key)
                }
            }
        }, to: url)
//#if DEBUG
//request
//    .responseString { response in
//        switch (response.result) {
//        case .success(let value):
//            print(value)
//            break
//        case .failure(let error):
//            print(Api.server + path + "\(error)")
//            break
//        }
//    }
//#endif
        return try await withCheckedThrowingContinuation { continuation in
            request
                .validate()
                .responseDecodable(of: JSONResponse<T>.self) { response in
                    switch response.result {
                        case .success(let value):
                        if value.code == -2 {
                            GlobalNotifier.default.needLogin.onNext(true)
                        }
                            continuation.resume(returning: value)
                            break
                        case .failure(let error):
                            continuation.resume(throwing: error)
                            break
                    }
                    
                }
        }
    }
    
    @discardableResult
    func post<T: Codable>(path: Api, param: [String: Any]?, dataType: T.Type) async throws -> JSONResponse<T> {
        let url = (self.server + path.rawValue + "?" + self.defaultParam).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? (self.server + path.rawValue + "?" + self.defaultParam)
        let header: HTTPHeaders = ["Content-Type": "application/x-www-form-urlencoded"]
        let request = AF.request(url, method: .post, parameters: param, headers: header, requestModifier: {$0.timeoutInterval = 10})
#if DEBUG
request
    .responseString { response in
        switch (response.result) {
        case .success(let value):
            print(value)
            break
        case .failure(let error):
            print(Api.server + path + "\(error)")
            break
        }
    }
#endif
        return try await withCheckedThrowingContinuation { continuation in
            request
                .validate()
                .responseDecodable(of: JSONResponse<T>.self) { response in
                    switch response.result {
                        case .success(let value):
                        if value.code == -2 {
                            GlobalNotifier.default.needLogin.onNext(true)
                        }
                            continuation.resume(returning: value)
                            break
                        case .failure(let error):
                            continuation.resume(throwing: error)
                            break
                    }
                    
                }
        }
    }
}
